let limit = 0 //帧率限制
let timeRunner = 0
let winWidth = window.innerWidth
let winHeight = window.innerHeight
let scene = new THREE.Scene()
let camera
let controls
let renderer = new THREE.WebGLRenderer({ antialias: true })
let mouse = new THREE.Vector2(-1, -1)
let raycaster
let strongBox
let ux, uy, uz
ux = uy = uz = 10 //游戏区域大小s
let countNum = 50,
  mineNum = countNum, //剩余雷的数目//雷的数量
  knowNum = 0 //记录多少方块已经挖开
let group = new THREE.Group()
let modules = []
let numData
let isDrag = false
let hoverMesh
let handleFlag = 'dig'
let hiddenList = []
let mobile = false //是否为移动设备
let moduleSize = 44
let materialColor
let theme = {
  color_bg: 0xe0e0e0,
  color_bg1: '#E0E0E0',
  color_md: 0xa0a0a0,
  color_1: '#AAA',
  color_2: '#CCC',
  music_bgm: undefined,
  music_game: undefined
}
let musicCtrl, gameLine

window.onload = function () {
  setTimeout(function () {
    if (document.getElementById('loading_box').style.visibility != 'hidden') {
      if (navigator.userAgent.indexOf('TBS') == '-1') {
        alert('未检测到x5内核，请稍后重启程序')
        let interval = setInterval(function () {
          if (navigator.userAgent.indexOf('TBS') != '-1') {
            location.reload()
          } else {
            clearInterval(interval)
          }
        }, 2000)
      }
    }
  }, 5000)
  initData()
  initParam()
  render()
  new MouseEvent()
  setTimeout(function () {
    document.getElementById('loading_box').style.visibility = 'hidden'
  }, 1000)
}
//初始化数据
function initData() {
  //初始化游戏参数
  let data = JSON.parse(localStorage.sweeperInit)
  if (data) {
    ux = parseInt(data.ux)
    uy = parseInt(data.uy)
    uz = parseInt(data.uz)
    mineNum = parseInt(data.mineNum)
    countNum = mineNum
  }
  data = JSON.parse(localStorage.gameTheme)
  if (data) {
    theme = data.color
      ? {
          color_bg: parseInt(data.color.color_bg, 16),
          color_bg1: data.color.color_bg1,
          color_md: parseInt(data.color.color_md, 16),
          color_1: data.color.color_1,
          color_2: data.color.color_2
        }
      : {
          color_bg: 0xe0e0e0,
          color_bg1: '#E0E0E0',
          color_md: 0xa0a0a0,
          color_1: '#AAA',
          color_2: '#CCC'
        }
    if (data.font) {
      document.body.classList.add('font-' + data.font.value)
    }
    theme.music_bgm = data.bgm ? data.bgm : undefined
    theme.music_game = data.game_music ? data.game_music : undefined
  }

  for (let dom of document.getElementsByClassName('btn_icon')) {
    dom.style.borderColor = theme.color_1
  }
  document.getElementById('hideBack').style.backgroundColor = theme.color_bg1
  mobile = /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)
}
//初始化参数
function initParam() {
  camera = new THREE.PerspectiveCamera(45, winWidth / winHeight, 1, moduleSize * 150)

  //初始布局
  if (mobile) {
    camera.position.set(moduleSize * 22, moduleSize * 25, moduleSize * 22)
  } else {
    camera.position.set(moduleSize * 12, moduleSize * 15, moduleSize * 12)
  }
  camera.lookAt(scene.position)

  //覆盖用方块
  let geometry = new THREE.BoxGeometry(moduleSize * 0.9, moduleSize * 0.9, moduleSize * 0.9)
  let material = new THREE.MeshLambertMaterial({
    color: 0xffffff
  })
  material.transparent = true
  material.opacity = 0.2 //透明度
  strongBox = new THREE.Mesh(geometry, material)
  strongBox.visible = false //可见度
  scene.add(strongBox)

  //创建方块组
  geometry = new THREE.BoxGeometry(moduleSize * 0.9, moduleSize * 0.9, moduleSize * 0.9)
  materialColor = new THREE.MeshLambertMaterial({
    color: theme.color_md
  })
  for (let x = 0; x < ux; x++) {
    let groupX = new THREE.Group()
    groupX.position.set(x * moduleSize, 0, 0)
    for (let y = 0; y < uy; y++) {
      let groupXY = new THREE.Group()
      groupXY.position.set(0, y * moduleSize, 0)
      for (let z = 0; z < uz; z++) {
        let mesh = new THREE.Mesh(geometry, materialColor)
        mesh.position.set(0, 0, z * moduleSize)
        mesh.name = (x > 9 ? '' : '0') + x + (y > 9 ? '' : '0') + y + (z > 9 ? '' : '0') + z
        mesh.castShadow = true
        mesh.receiveShadow = true
        mesh.know = false
        groupXY.add(mesh)
        modules.push(mesh)
      }
      groupX.add(groupXY)
    }
    group.add(groupX)
  }
  group.position.set((-(ux - 1) / 2) * moduleSize, (-(uy - 1) / 2) * moduleSize, (-(uz - 1) / 2) * moduleSize)
  scene.add(group)

  let light = new THREE.PointLight(0xffffff, 1)
  light.position.set(moduleSize * 20, moduleSize * 25, moduleSize * 22)
  light.target = group
  scene.add(light)
  light = new THREE.PointLight(0xffffff, 1)
  light.position.set(-moduleSize * 20, -moduleSize * 25, -moduleSize * 22)
  light.target = scene
  light.target = group
  scene.add(light)

  let ambient = new THREE.AmbientLight(0x444444)
  scene.add(ambient) //环境光

  renderer.setSize(winWidth, winHeight)
  //开启阴影功能
  // renderer.shadowMap.enabled = true
  renderer.setClearColor(theme.color_bg, 1)
  document.body.appendChild(renderer.domElement)
}
//刷新渲染
function render() {
  requestAnimationFrame(render)
  //更新显示文本内容
  document.getElementById('countNum').innerText = countNum
  if (timeRunner == 0) {
    document.getElementById('timeRunner').innerText = '点击计时'
  } else {
    let ms = timeRunner % 100
    let ss = parseInt(timeRunner / 100) % 60
    let mm = parseInt(timeRunner / 6000) % 60
    let hh = parseInt(timeRunner / 360000)
    if (hh > 0) {
      document.getElementById('timeRunner').innerText =
        (hh > 9 ? '' : '0') + hh + ':' + (mm > 9 ? '' : '0') + mm + ':' + (ss > 9 ? '' : '0') + ss
    } else {
      document.getElementById('timeRunner').innerText =
        (mm > 9 ? '' : '0') + mm + ':' + (ss > 9 ? '' : '0') + ss + ':' + (ms > 9 ? '' : '0') + ms
    }
  }

  //限制帧数
  limit = (limit + 1) % 2
  if (limit == 0) {
    if (controls) {
      controls.update()
    }
    renderer.render(scene, camera)
  }
}
//鼠标控制事件
class MouseEvent {
  constructor() {
    if (mobile) {
      //移动端操作函数
      this.stamp = 0
      renderer.domElement.addEventListener('touchstart', this.onTouchStart)
      renderer.domElement.addEventListener('touchmove', this.onTouchMove)
      renderer.domElement.addEventListener('touchend', this.onTouchEnd)
    } else {
      //pc端操作函数
      renderer.domElement.addEventListener('mousemove', this.onMouseMove)
      renderer.domElement.addEventListener('mousedown', this.onMouseDown)
      renderer.domElement.addEventListener('mouseup', this.onMouseUp)
    }
    controls = new THREE.TrackballControls(camera, renderer.domElement)

    // controls.rotateSpeed = 2.0
  }
  onMouseMove(event) {
    //记录鼠标的二维坐标
    mouse.x = (event.clientX / winWidth) * 2 - 1
    mouse.y = -(event.clientY / winHeight) * 2 + 1
    isDrag = true
    MouseEvent.mouseChange()
  }
  onMouseDown() {
    isDrag = false
  }
  onMouseUp() {
    if (!isDrag) {
      //点击事件
      if (hoverMesh) {
        //如果点中立方体
        if (!numData) {
          //初始化游戏
          MouseEvent.initGame(hoverMesh)
          new MeshCaver()
        }
        //触发点击事件
        MouseEvent.handleClick(hoverMesh, handleFlag)
        MouseEvent.mouseChange()
      }
    } //拖拽事件
  }
  onTouchMove(event) {
    mouse.x = (event.changedTouches[0].clientX / winWidth) * 2 - 1
    mouse.y = -(event.changedTouches[0].clientY / winHeight) * 2 + 1
    if (parseInt(event.timeStamp / 10) > parseInt(this.stamp / 10)) {
      //一秒最多渲染十次
      MouseEvent.mouseChange()
    }
    this.stamp = event.timeStamp
    isDrag = true
  }
  onTouchStart() {
    isDrag = false
  }
  onTouchEnd(event) {
    mouse.x = (event.changedTouches[0].clientX / winWidth) * 2 - 1
    mouse.y = -(event.changedTouches[0].clientY / winHeight) * 2 + 1
    MouseEvent.mouseChange()
    if (!isDrag) {
      //点击事件
      if (hoverMesh) {
        //如果点中立方体
        if (!numData) {
          //初始化游戏
          MouseEvent.initGame(hoverMesh)
          new MeshCaver()
        }
        //触发点击事件
        MouseEvent.handleClick(hoverMesh, handleFlag)
        MouseEvent.mouseChange()
      }
    }
  }
  static initGame(mesh) {
    musicCtrl = new MusicCtrl()
    let site = {
      //记录初始点坐标
      x: parseInt(parseInt(mesh.name) / 10000) % 100,
      y: parseInt(parseInt(mesh.name) / 100) % 100,
      z: parseInt(mesh.name) % 100
    }
    //数组初始化
    numData = new Array()
    for (let x = 0; x < ux; x++) {
      numData[x] = new Array()
      for (let y = 0; y < uy; y++) {
        //这种写法可以初始化为0
        numData[x][y] = new Array(uz).fill(0)
      }
    }
    //随机生成雷
    while (mineNum > 0) {
      let rem = {
        x: Math.floor(Math.random() * ux),
        y: Math.floor(Math.random() * uy),
        z: Math.floor(Math.random() * uz)
      }
      //雷未生成且非初始点
      if (numData[rem.x][rem.y][rem.z] == 0 && (rem.x != site.x || rem.y != site.y || rem.z != site.z)) {
        numData[rem.x][rem.y][rem.z] = -1
        mineNum--
      }
    }
    mineNum = countNum
    //判断每个面的数字
    for (let x = 0; x < ux; x++) {
      for (let y = 0; y < uy; y++) {
        for (let z = 0; z < uz; z++) {
          if (numData[x][y][z] == -1) continue //此处必须用continue，break会跳出三层
          //右面yz,占立方第1、2个面
          let yz = 0
          if (isMine(x, y - 1, z - 1)) yz++
          if (isMine(x, y - 1, z)) yz++
          if (isMine(x, y - 1, z + 1)) yz++

          if (isMine(x, y, z - 1)) yz++
          if (isMine(x, y, z + 1)) yz++

          if (isMine(x, y + 1, z - 1)) yz++
          if (isMine(x, y + 1, z)) yz++
          if (isMine(x, y + 1, z + 1)) yz++
          //上面xz,占立方第3、4个面
          let xz = 0
          if (isMine(x - 1, y, z - 1)) xz++
          if (isMine(x - 1, y, z)) xz++
          if (isMine(x - 1, y, z + 1)) xz++

          if (isMine(x, y, z - 1)) xz++
          if (isMine(x, y, z + 1)) xz++

          if (isMine(x + 1, y, z - 1)) xz++
          if (isMine(x + 1, y, z)) xz++
          if (isMine(x + 1, y, z + 1)) xz++
          //左面xy,占立方第5、6个面
          let xy = 0
          if (isMine(x - 1, y - 1, z)) xy++
          if (isMine(x - 1, y, z)) xy++
          if (isMine(x - 1, y + 1, z)) xy++

          if (isMine(x, y - 1, z)) xy++
          if (isMine(x, y + 1, z)) xy++

          if (isMine(x + 1, y - 1, z)) xy++
          if (isMine(x + 1, y, z)) xy++
          if (isMine(x + 1, y + 1, z)) xy++
          numData[x][y][z] = xy * 100 + xz * 10 + yz
          //判断该坐标的合法性及是否是雷
          function isMine(x, y, z) {
            if (numData[x] && numData[x][y] && numData[x][y][z] && numData[x][y][z] == -1) return true
            else return false
          }
        }
      }
    }
    //计时器开始计时
    gameLine = new GameLine()
  }
  static protect = 0
  static handleClick(meshObj, state) {
    switch (state) {
      case 'dig': //挖掘
        //如果被插旗或者已经挖过则不挖
        if (meshObj.flag || meshObj.know || !meshObj.visible) {
          return
        }
        musicCtrl.digAudio()
        handleDig(meshObj)
        break
      case 'flag': //插旗
        //如果已知的话不能插旗也不能取消
        if (meshObj.know) {
          return
        }
        musicCtrl.flagAudio()
        if (meshObj.flag) {
          meshObj.material = materialColor
          meshObj.flag = false
          countNum++
        } else {
          meshObj.material = MeshCaver.numCovers[10]
          meshObj.flag = true
          countNum--
        }
        GameLine.ifWin()
        break
      case 'hide': //隐藏
        musicCtrl.hiddenAudio()
        hiddenList.push(meshObj)
        meshObj.visible = false
        document.getElementById('hideBack').style.backgroundColor = 'white'
        break
    }
    function handleDig(meshObj) {
      let site = {
        x: parseInt(meshObj.name / 10000) % 100,
        y: parseInt(meshObj.name / 100) % 100,
        z: meshObj.name % 100
      }
      //可以挖掘的方块设置为已知
      meshObj.know = true
      knowNum++
      switch (numData[site.x][site.y][site.z]) {
        case -1: //游戏结束
          gameLine.gameLose()
          return
        case 0:
          //首先隐藏方块
          meshObj.visible = false
          //判断能否获胜
          if (knowNum + mineNum == ux * uy * uz) {
            //这种情况只有憨憨先插旗最后挖掉的时候会出现吧
            gameLine.youWin()
            return
          }
          //计时保护开始
          let runtime //loading时的时间补偿
          if (MouseEvent.protect == 0) {
            setTimeout(function () {
              document.getElementById('loading_box').style.visibility = 'visible'
              runtime = new Date().getTime()
            })
          }
          MouseEvent.protect++
          doClick(site.x - 1, site.y - 1, site.z)
          doClick(site.x - 1, site.y, site.z)
          doClick(site.x - 1, site.y + 1, site.z)
          doClick(site.x, site.y - 1, site.z)
          doClick(site.x, site.y + 1, site.z)
          doClick(site.x + 1, site.y - 1, site.z)
          doClick(site.x + 1, site.y, site.z)
          doClick(site.x + 1, site.y + 1, site.z)
          doClick(site.x, site.y, site.z - 1)
          doClick(site.x, site.y, site.z + 1)
          doClick(site.x - 1, site.y, site.z - 1)
          doClick(site.x + 1, site.y, site.z - 1)
          doClick(site.x - 1, site.y, site.z + 1)
          doClick(site.x + 1, site.y, site.z + 1)
          doClick(site.x, site.y - 1, site.z - 1)
          doClick(site.x, site.y + 1, site.z - 1)
          doClick(site.x, site.y - 1, site.z + 1)
          doClick(site.x, site.y + 1, site.z + 1)
          //计时保护结束
          MouseEvent.protect--
          if (MouseEvent.protect == 0) {
            setTimeout(function () {
              document.getElementById('loading_box').style.visibility = 'hidden'
              //计时补偿
              runtime = new Date().getTime() - runtime
              GameLine.beginTime = GameLine.beginTime - runtime
            }, 100)
          }
          function doClick(x, y, z) {
            //先判断存不存在,能不能点
            if (x < 0 || x >= ux || y < 0 || y >= uy || z < 0 || z >= uz) {
              return
            }
            let mesh = group.children[x].children[y].children[z]
            //判断能不能挖
            if (mesh.flag || mesh.know || !mesh.visible) {
              return
            }
            if (mesh.visible) {
              handleDig(mesh)
            }
          }
          break
        default:
          //其余数字
          let faceNum = {
            x: numData[site.x][site.y][site.z] % 10,
            y: parseInt(numData[site.x][site.y][site.z] / 10) % 10,
            z: parseInt(numData[site.x][site.y][site.z] / 100) % 10
          }
          meshObj.material = [
            MeshCaver.numCovers[faceNum.x],
            MeshCaver.numCovers[faceNum.x],
            MeshCaver.numCovers[faceNum.y],
            MeshCaver.numCovers[faceNum.y],
            MeshCaver.numCovers[faceNum.z],
            MeshCaver.numCovers[faceNum.z]
          ]
          //判断周围的雷能否确定
          doFlag(site.x - 1, site.y - 1, site.z)
          doFlag(site.x - 1, site.y, site.z)
          doFlag(site.x - 1, site.y + 1, site.z)
          doFlag(site.x - 1, site.y, site.z - 1)
          doFlag(site.x - 1, site.y, site.z + 1)
          //
          doFlag(site.x, site.y - 1, site.z)
          doFlag(site.x, site.y + 1, site.z)
          //
          doFlag(site.x + 1, site.y - 1, site.z)
          doFlag(site.x + 1, site.y, site.z)
          doFlag(site.x + 1, site.y + 1, site.z)
          doFlag(site.x + 1, site.y, site.z - 1)
          doFlag(site.x + 1, site.y, site.z + 1)
          //
          doFlag(site.x, site.y - 1, site.z - 1)
          doFlag(site.x, site.y, site.z - 1)
          doFlag(site.x, site.y + 1, site.z - 1)
          //
          doFlag(site.x, site.y - 1, site.z + 1)
          doFlag(site.x, site.y, site.z + 1)
          doFlag(site.x, site.y + 1, site.z + 1)
          function doFlag(x, y, z) {
            //判断坐标合法性
            if (x < 0 || x >= ux || y < 0 || y >= uy || z < 0 || z >= uz) {
              return false
            }
            if (numData[x][y][z] != -1) {
              //不是雷直接返回
              return false
            }
            //如果这个雷之前被确定过了，直接返回
            if (group.children[x].children[y].children[z].know) {
              return false
            }
            //判断该雷能否判断为已知(周围方块都是已知的)
            if (
              ifMesh(x - 1, y, z - 1) &&
              ifMesh(x - 1, y, z) &&
              ifMesh(x - 1, y, z + 1) &&
              ifMesh(x - 1, y - 1, z) &&
              ifMesh(x - 1, y + 1, z) &&
              //
              ifMesh(x, y, z - 1) &&
              ifMesh(x, y, z + 1) &&
              //
              ifMesh(x + 1, y, z - 1) &&
              ifMesh(x + 1, y, z) &&
              ifMesh(x + 1, y, z + 1) &&
              ifMesh(x + 1, y - 1, z) &&
              ifMesh(x + 1, y + 1, z) &&
              //
              ifMesh(x, y - 1, z - 1) &&
              ifMesh(x, y - 1, z) &&
              ifMesh(x, y - 1, z + 1) &&
              //
              ifMesh(x, y + 1, z - 1) &&
              ifMesh(x, y + 1, z) &&
              ifMesh(x, y + 1, z + 1)
            ) {
              //注意，仅仅插旗不会设为已知，必须识别之后才能设置，且此时的插旗状态不能取消
              group.children[x].children[y].children[z].know = true
              group.children[x].children[y].children[z].material = MeshCaver.numCovers[10]
              if (!group.children[x].children[y].children[z].flag) {
                countNum--
                //如果插完旗判断当前胜负情况
                GameLine.ifWin()
              }
              return true
            }
            function ifMesh(x1, y1, z1) {
              //判断坐标合法性，不合法坐标默认为已知
              if (x1 < 0 || x1 >= ux || y1 < 0 || y1 >= uy || z1 < 0 || z1 >= uz) {
                return true
              }
              if (numData[x1][y1][z1] == -1) {
                //假装自己已知判断其他的能否为已知
                group.children[x].children[y].children[z].know = true
                doFlag(x1, y1, z1)
                group.children[x].children[y].children[z].know = false
              }
              return group.children[x1].children[y1].children[z1].know
            }
          }
          //判断能否获胜
          if (knowNum + mineNum == ux * uy * uz) {
            gameLine.youWin()
            return
          }
          break
      }
    }
  }
  //射线捕获模型并且刷新高亮
  static mouseChange() {
    if (!raycaster) {
      raycaster = new THREE.Raycaster()
    }
    raycaster.setFromCamera(mouse, camera)
    const intersects = raycaster.intersectObjects(modules)
    //移动方块并显示
    if (intersects[0]) {
      for (let item of intersects) {
        if (item.object.visible) {
          hoverMesh = item.object
          strongBox.position.set(
            hoverMesh.parent.parent.position.x + group.position.x,
            hoverMesh.parent.position.y + group.position.y,
            hoverMesh.position.z + group.position.z
          )
          strongBox.visible = true
          break
        }
        strongBox.visible = false
        hoverMesh = undefined
      }
    } else {
      strongBox.visible = false
      hoverMesh = undefined
    }
  }
}
//按钮相关操作的方法类
class HandleBtn {
  static floors = []
  //更改按钮状态
  static btnHandleFlag(dom, handle) {
    let ctrlBtn = document.getElementsByClassName('btnCtrl')
    for (let btn of ctrlBtn) {
      btn.classList.remove('btn_on_select')
    }
    dom.classList.add('btn_on_select')
    handleFlag = handle
  }
  //撤销隐藏
  static hideBack() {
    if (hiddenList.length > 0) {
      hiddenList.pop().visible = true
    }
    if (hiddenList.length == 0) {
      document.getElementById('hideBack').style.backgroundColor = theme.color_bg1
    }
  }
  //层级隐藏
  static floorHidden() {
    let someMesh = []
    let y = uy - HandleBtn.floors.length - 1
    if (y <= 0) return
    for (let x = 0; x < ux; x++) {
      for (let z = 0; z < uz; z++) {
        if (group.children[x].children[y].children[z].visible == true) {
          someMesh.push(group.children[x].children[y].children[z])
          group.children[x].children[y].children[z].visible = false
        }
      }
    }
    HandleBtn.floors.push(someMesh)
  }
  //层级显示
  static floorShow() {
    if (HandleBtn.floors.length == 0) return
    HandleBtn.floors.pop().forEach(element => {
      element.visible = true
    })
  }
  static refreshCamera() {
    controls.reset()
  }
}
//游戏流程控制的方法类
class GameLine {
  static timer
  static beginTime
  constructor() {
    GameLine.beginTime = new Date().getTime()
    GameLine.timer = setInterval(function () {
      timeRunner = parseInt((new Date().getTime() - GameLine.beginTime) / 10)
    }, 10)
  }
  static ifWin() {
    //如果插完旗判断当前胜负情况
    if (countNum == 0) {
      for (let x = 0; x < ux; x++) {
        for (let y = 0; y < uy; y++) {
          for (let z = 0; z < uz; z++) {
            if (numData[x][y][z] == -1) {
              //该位置的方块是否插旗
              let mesh = group.children[x].children[y].children[z]
              if (!mesh.flag) {
                return
              }
            }
          }
        }
      }
      //全部符合要求则判胜
      gameLine.youWin()
    }
  }
  youWin() {
    musicCtrl.winAudio()
    for (let x = 0; x < ux; x++) {
      for (let y = 0; y < uy; y++) {
        for (let z = 0; z < uz; z++) {
          if (numData[x][y][z] == -1) {
            group.children[x].children[y].children[z].material = MeshCaver.numCovers[10]
          }
        }
      }
    }
    setTimeout(() => {
      alert('游戏通关,刷新页面重新开始游戏!')
    }, 200)
    clearInterval(GameLine.timer)
    countNum = 'WIN'
  }
  gameLose() {
    musicCtrl.overAudio()
    for (let x = 0; x < ux; x++) {
      for (let y = 0; y < uy; y++) {
        for (let z = 0; z < uz; z++) {
          if (numData[x][y][z] == -1) {
            group.children[x].children[y].children[z].material = MeshCaver.numCovers[12]
          }
        }
      }
    }
    setTimeout(() => {
      alert('游戏结束,刷新页面重新开始游戏!')
    }, 200)
    clearInterval(GameLine.timer)
    countNum = 'LOSE'
  }
}
// 数字问号及炸弹旗子符号的绘制
class MeshCaver {
  static numColors = ['white', 'blue', 'green', 'red', 'DarkBlue', 'DarkRed', 'DarkCyan', 'black', 'gray']
  static numCovers = []
  constructor() {
    this.initNumCover()
  }
  // 数字问号及炸弹旗子符号的绘制
  initNumCover() {
    //绘制数字和问号
    let numCover, ctx, texture
    for (let i = 0; i < 10; i++) {
      numCover = document.createElement('canvas')
      numCover.width = 16
      numCover.height = 16
      ctx = numCover.getContext('2d')
      ctx.fillStyle = theme.color_1
      ctx.fillRect(0, 0, 16, 16)
      ctx.fillStyle = theme.color_2
      ctx.fillRect(1, 1, 14, 14)
      ctx.fillStyle = MeshCaver.numColors[i] ? MeshCaver.numColors[i] : 'black' //文本填充颜色
      ctx.font = '14px' //字体样式设置
      ctx.textBaseline = 'middle' //文本与fillText定义的纵坐标
      ctx.textAlign = 'center' //文本居中(以fillText定义的横坐标)
      if (i < 9) ctx.fillText(i, 8, 9)
      else ctx.fillText('?', 8, 9)
      texture = new THREE.CanvasTexture(numCover)
      texture = new THREE.MeshLambertMaterial({ map: texture })
      MeshCaver.numCovers.push(texture)
      // document.body.appendChild(numCover);
    }
    //绘制旗子
    numCover = document.createElement('canvas')
    numCover.width = 16
    numCover.height = 16
    ctx = numCover.getContext('2d')
    ctx.fillStyle = theme.color_1
    ctx.fillRect(0, 0, 16, 16)
    ctx.fillStyle = theme.color_2
    ctx.fillRect(1, 1, 14, 14)
    ctx.fillStyle = '#F00'
    ctx.moveTo(9.5, 2)
    ctx.lineTo(4, 6)
    ctx.lineTo(9.5, 9)
    ctx.lineTo(9.5, 2)
    ctx.fill()
    ctx.beginPath()
    ctx.fillStyle = '#000'
    ctx.moveTo(9, 9)
    ctx.lineTo(9, 13)
    ctx.moveTo(5, 12)
    ctx.lineTo(12, 12)
    ctx.moveTo(3, 13)
    ctx.lineTo(14, 13)
    ctx.stroke()
    texture = new THREE.CanvasTexture(numCover)
    texture = new THREE.MeshLambertMaterial({ map: texture })
    MeshCaver.numCovers.push(texture)
    // document.body.appendChild(numCover);
    //绘制地雷(完好)
    numCover = document.createElement('canvas')
    numCover.width = 16
    numCover.height = 16
    ctx = numCover.getContext('2d')
    ctx.fillStyle = theme.color_1
    ctx.fillRect(0, 0, 16, 16)
    ctx.fillStyle = theme.color_2
    ctx.fillRect(1, 1, 14, 14)
    ctx.fillStyle = '#000'
    ctx.moveTo(3, 3)
    ctx.lineTo(13, 13)
    ctx.moveTo(3, 13)
    ctx.lineTo(13, 3)
    ctx.moveTo(8, 1)
    ctx.lineTo(8, 15)
    ctx.moveTo(1, 8)
    ctx.lineTo(15, 8)
    ctx.stroke()
    ctx.arc(8, 8, 5, 0, 2 * Math.PI)
    ctx.fill()
    ctx.fillStyle = '#FFF'
    ctx.fillRect(5, 5, 2, 2)
    texture = new THREE.CanvasTexture(numCover)
    texture = new THREE.MeshLambertMaterial({ map: texture })
    MeshCaver.numCovers.push(texture)
    // document.body.appendChild(numCover);
    //绘制地雷(炸开)
    numCover = document.createElement('canvas')
    numCover.width = 16
    numCover.height = 16
    ctx = numCover.getContext('2d')
    ctx.fillStyle = theme.color_1
    ctx.fillRect(0, 0, 16, 16)
    ctx.fillStyle = '#F00'
    ctx.fillRect(1, 1, 14, 14)
    ctx.fillStyle = '#000'
    ctx.moveTo(3, 3)
    ctx.lineTo(13, 13)
    ctx.moveTo(3, 13)
    ctx.lineTo(13, 3)
    ctx.moveTo(8, 1)
    ctx.lineTo(8, 15)
    ctx.moveTo(1, 8)
    ctx.lineTo(15, 8)
    ctx.stroke()
    ctx.arc(8, 8, 5, 0, 2 * Math.PI)
    ctx.fill()
    ctx.fillStyle = '#FFF'
    ctx.fillRect(5, 5, 2, 2)
    texture = new THREE.CanvasTexture(numCover)
    texture = new THREE.MeshLambertMaterial({ map: texture })
    MeshCaver.numCovers.push(texture)
    // document.body.appendChild(numCover);
  }
}
class MusicCtrl {
  bgmAudio
  gameAudio
  bgmInfo
  gameInfo
  constructor() {
    if (theme.music_bgm) {
      this.bgmInfo = theme.music_bgm
      this.bgmAudio = document.createElement('audio')
      this.bgmAudio.src = '..' + this.bgmInfo.url
      this.bgmAudio.loop = true
      this.bgmAudio.play()
    }
    if (theme.music_game) {
      this.gameAudio = document.createElement('audio')
      this.gameInfo = theme.music_game
    }
  }
  //挖掘音效
  digAudio() {
    if (this.gameInfo) {
      this.gameAudio.src = '..' + this.gameInfo.dig
      this.gameAudio.play()
    }
  }
  //插旗音效
  flagAudio() {
    if (this.gameInfo) {
      this.gameAudio.src = '..' + this.gameInfo.flag
      this.gameAudio.play()
    }
  }
  //隐藏音效
  hiddenAudio() {
    if (this.gameInfo) {
      this.gameAudio.src = '..' + this.gameInfo.hidden
      this.gameAudio.play()
    }
  }
  //游戏结束音效
  overAudio() {
    if (this.gameInfo) {
      let that = this
      setTimeout(function () {
        that.gameAudio.src = '..' + that.gameInfo.over
        that.gameAudio.play()
      }, 100)
    }
  }
  //游戏获胜音效
  winAudio() {
    if (this.gameInfo) {
      let that = this
      setTimeout(function () {
        that.gameAudio.src = '..' + that.gameInfo.win
        that.gameAudio.play()
      }, 100)
    }
  }
}
